﻿using Core.Info;
using Core.Utility.Helper;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Resources;
using Core.Language.Language;
using System.Text.RegularExpressions;
using Core.Utility.Extendsions.Regex;
using Core.Utility;
using Core.Log;
using Microsoft.Extensions.Logging;

namespace Core.Language
{
    public class CurrentUICultureChangedEventArgs : EventArgs
    {
        public CultureInfo OldUICulture { get; }

        public CultureInfo NewUICulture { get; }

        public CurrentUICultureChangedEventArgs(CultureInfo oldUiCulture, CultureInfo newUiCulture)
        {
            OldUICulture = oldUiCulture;
            NewUICulture = newUiCulture;
        }
    }

    /// <summary>
    /// 语言资源
    /// 默认区域信息名称： zh-CN <see cref="DefaultCultureInfo"/>
    /// </summary>
    public class LanguageResource
    {
        /// <summary>
        /// 默认区域信息名称
        /// </summary>
        public static readonly CultureInfo DefaultCultureInfo = new CultureInfo("zh-CN");

        private static readonly object _lock = new object();

        /// <summary>
        /// 无资源值包裹字符串
        /// </summary>
        public static readonly string NoResourceValueParcelString = "[<-!->]";

        public static event EventHandler<CurrentUICultureChangedEventArgs>? CurrentUICultureChanged;



        /// <summary>
        /// 静态资源构造
        /// </summary>
        static LanguageResource()
        {
            //默认语言资源无 CultureInfo，强制设置为 zh-CN
            //语言列表：https://docs.microsoft.com/zh-cn/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c
            Source.TryAdd(DefaultCultureInfo.Name, new ConcurrentDictionary<string, string?>());
            LoadLocalFileResource();
        }

        /// <summary>
        /// 当前UICulture变跟时
        /// </summary>
        /// <param name="oldCulture"></param>
        /// <param name="newCulture"></param>
        public static void OnCurrentUICultureChanged(object sender, CultureInfo oldCulture, CultureInfo newCulture)
        {
            CurrentUICultureChanged?.Invoke(sender, new CurrentUICultureChangedEventArgs(oldCulture, newCulture));
        }

        /// <summary>
        /// 语言源数据
        /// </summary>
        public static ConcurrentDictionary<string, ConcurrentDictionary<string, string?>> Source
        {
            get;
            private set;
        } = new ConcurrentDictionary<string, ConcurrentDictionary<string, string?>>();

        /// <summary>
        /// 获取 <see cref="Source"/> 中添加的 <see cref="CultureInfo"/> 列表
        /// </summary>
        public static List<CultureInfo?>? SourceCultureInfoList
        {
            get
            {
                var res = new List<CultureInfo?>();
                if (Source != null)
                {
                    List<string> sourceCultureInfoNameList = Source.Keys.ToList();
                    for(int i = 0; i < sourceCultureInfoNameList.Count; i++)
                    {
                        sourceCultureInfoNameList[i] = sourceCultureInfoNameList[i].ToLower();
                        try
                        {
                            res.Add(new CultureInfo(sourceCultureInfoNameList[i]));
                        }
                        catch
                        {

                        }
                    }
                    var allCultureInfoList = CultureInfoHelper.AllCultureInfoList;
                    var findList = allCultureInfoList.Where(p => sourceCultureInfoNameList.Contains(p.Name.ToLower())).ToList();
                    foreach(var item in findList)
                    {
                        if(res.Count(p=>p?.Name == item.Name) <= 0)
                        {
                            res.Add(item);
                        }
                    }
                }
                res = res.OrderBy(p=>p?.Name).ToList();
                return res;
            }
        }

        /// <summary>
        /// 加载本地文件资源
        /// </summary>
        private static void LoadLocalFileResource()
        {
            try
            {
                //语言JSON文件
                List<string> languageJsonFileList = FileHelper.WildcardFindFileFullPath(AppInfo.LanguageDirectory, "*.language.json");
                foreach (var languageJsonFile in languageJsonFileList)
                {
                    string cultureInfoName = Path.GetFileNameWithoutExtension(languageJsonFile);
                    cultureInfoName = Path.GetFileNameWithoutExtension(cultureInfoName);
                    //加载资源
                    ConcurrentDictionary<string, string>? languageDictionary = new ConcurrentDictionary<string, string>();
                    if (File.Exists(languageJsonFile))
                    {
                        string? languageJson = FileHelper.ReadFileToString(languageJsonFile);
                        if (!string.IsNullOrEmpty(languageJson))
                        {
                            try
                            {
                                languageDictionary = JsonHelper.Deserialize<ConcurrentDictionary<string, string>>(languageJson);
                            }
                            catch (Exception ex)
                            {
                                AppTrace.Logger.LogError(ex, $"加载本地语言资源文件失败，请查证文件内容是否合法，文件路径：\r\n{languageJsonFile}");
                            }
                        }
                    }
#pragma warning disable CS8604 // 引用类型参数可能为 null。
#pragma warning disable CS8620 // 由于引用类型的可为 null 性差异，实参不能用于形参。
                    Source.AddOrUpdate(cultureInfoName, languageDictionary);
#pragma warning restore CS8620 // 由于引用类型的可为 null 性差异，实参不能用于形参。
#pragma warning restore CS8604 // 引用类型参数可能为 null。
                }
            }
            catch (Exception ex)
            {
                AppTrace.Logger.LogError(ex, $"加载本地语言资源文件失败，请查证文件内容是否合法，目录路径：\r\n{AppInfo.LanguageDirectory}");
            }
        }


        /// <summary>
        /// 添加资源
        /// </summary>
        /// <param name="ciName">区域特性名称，参见：<see cref="CultureInfo.Name"/> </param>
        /// <param name="key">资源Key</param>
        /// <param name="value">资源值</param>
        /// <param name="isAddOrUpdate">是否启用新增或更新逻辑，当值为true时，资源的Key值存在，则将值更新为当前参数传入的值</param>
        public static void AddResource(CultureInfo ci, string key, string value, bool isAddOrUpdate = false)
        {
            AddResource(ci.Name,key,value,isAddOrUpdate);
        }

        /// <summary>
        /// 添加资源
        /// </summary>
        /// <param name="ciName">区域特性名称，参见：<see cref="CultureInfo.Name"/> </param>
        /// <param name="key">资源Key</param>
        /// <param name="value">资源值</param>
        /// <param name="isAddOrUpdate">是否启用新增或更新逻辑，当值为true时，资源的Key值存在，则将值更新为当前参数传入的值</param>
        public static void AddResource(string ciName, string key, string value, bool isAddOrUpdate = false)
        {
            AddResource(
                ciName,
                new Dictionary<string, string?>()
                {
                    {key,value },
                },
                isAddOrUpdate);
        }


        /// <summary>
        /// 添加资源
        /// </summary>
        /// <param name="ci">区域特性，参见：<see cref="CultureInfo"/> </param>
        /// <param name="source">资源</param>
        /// <param name="isAddOrUpdate">是否启用新增或更新逻辑，当值为true时，资源的Key值存在，则将值更新为当前参数传入的值</param>
        public static void AddResource(CultureInfo ci, Dictionary<string, string?> source, bool isAddOrUpdate = false)
        {
            AddResource(ci.Name, source, isAddOrUpdate);
        }

        /// <summary>
        /// 添加资源
        /// </summary>
        /// <param name="ciName">区域特性名称，参见：<see cref="CultureInfo.Name"/> </param>
        /// <param name="source">资源</param>
        /// <param name="isAddOrUpdate">是否启用新增或更新逻辑，当值为true时，资源的Key值存在，则将值更新为当前参数传入的值</param>
        public static IReturnResultInfo AddResource(string? ciName, Dictionary<string,string?> source, bool isAddOrUpdate = false)
        {
            IReturnResultInfo res = new LanguageReturnResultInfo();
            var oldCiName = ciName;
            ciName = CultureInfoHelper.GetCultureInfoName(ciName);
            if(ciName == "zh-Hans-CN")
            {
                if(Source?.ContainsKey("zh-Hans-CN") == false)
                {
                    ciName = "zh-CN";
                }
            }
            if(string.IsNullOrEmpty(ciName))
            {
                res.Message = StringFormat(
                    $"参数:{nameof(ciName)} 的值：{0} 不合法，语言资源添加(LanguageResource.AddResource)失败，请查证！", 
                    isConverterArgs: false,
                    args: new object?[]
                    {
                        oldCiName
                    });
                return res;
            }
            try
            {
                ConcurrentDictionary<string, string?> dict = new ConcurrentDictionary<string, string?>();
                lock (_lock)
                {
#pragma warning disable CS8602 // 解引用可能出现空引用。
                    dict = Source.GetOrAdd(ciName, dict);
#pragma warning restore CS8602 // 解引用可能出现空引用。
                    if (source != null)
                    {
                        if (isAddOrUpdate)
                        {
                            foreach (var item in source)
                            {
                                dict.AddOrUpdate(item.Key, item.Value);
                            }
                        }
                        else
                        {
                            foreach (var item in source)
                            {
                                dict.TryAdd(item.Key, item.Value);
                            }
                        }
                    }
                    Source.AddOrUpdate(ciName, dict);
                }
            }
            catch(Exception ex)
            {
                res.Message = StringFormat("语言资源添加(LanguageResource.AddResource)失败，\r\n{0}\r\n请查证！",isConverterArgs:false,args:ex.DetailMessage());
                res.Info = $"{res.Message}\r\n{res.Message}";
            }
            res.Successed = true;
            return res;
        }

        /// <summary>
        /// 获取资源值；
        /// <para>获取顺序：<see cref="LanguageResource.Source"/> =&gt; <see cref="Language.Language.ResourceManager"/> </para>
        /// <para>其中：<see cref="LanguageResource.Source"/> 加载内容通过读取 RootDir/Language/*.language.json 文件 或 读取DB/语言资源接口 决定最终值 </para>
        /// </summary>
        /// <param name="key">资源的键</param>
        /// <param name="cultureInfo">区域设置，为 default 取当前线程的值， <see cref="CultureInfo.CurrentCulture"/> </param>
        /// <param name="tryParents">
        /// true 表示使用资源回退加载相应资源（如果找不到资源）；
        /// false 表示绕过资源回退进程;
        /// <see cref="ResourceSet.GetString(string, bool)"/>
        /// </param>
        /// <param name="isReturnNoResourceValueParcelString">是否返回无资源值替换字符串</param>
        /// <returns></returns>
        public static string? GetResourceValue(string? key, CultureInfo? cultureInfo = default,bool tryParents = false,bool isReturnNoResourceValueParcelString = true)
        {
            if(string.IsNullOrEmpty(key))
            {
                return key;
            }
            if(cultureInfo == default)
            {
                cultureInfo = CultureInfo.CurrentUICulture;
                if(cultureInfo.Name == "zh-Hans-CN")
                {
                    cultureInfo = DefaultCultureInfo;
                }
            }
            key = key.Replace(NoResourceValueParcelString, "");
            string? res = $"{NoResourceValueParcelString}{key}{NoResourceValueParcelString}";
            bool isFindResourceManage = false;
            var _resourceManagerStorage = LanguageResource.Source;
            if (_resourceManagerStorage != null && _resourceManagerStorage.Keys.Contains(cultureInfo.Name))
            {
                if (_resourceManagerStorage[cultureInfo.Name].ContainsKey(key))
                {
                    res = _resourceManagerStorage[cultureInfo.Name][key];
                }
                else
                {
                    isFindResourceManage = true;
                }
            }
            else
            {
                isFindResourceManage = true;
            }
            if (isFindResourceManage)
            {
                try
                {                    
                    ResourceSet? rs = Language.Language.ResourceManager.GetResourceSet(cultureInfo, true, tryParents);
                    string? value = null;
                    if (rs == null && cultureInfo.Name == DefaultCultureInfo.Name)
                    {
                        //当rs为null，且cultureInfo.Name == DefaultCultureInfo.Name时；
                        //由于Language.Language.Culture = null;故，不再使用 ResourceSet.GetString(key)获取翻译值
                        value = Language.Language.ResourceManager.GetString(key);
                    }
                    else
                    {
                        value = rs?.GetString(key);
                    }
                    if (value != null)
                    {
                        //允许value为string.Empty;
                        res = value;
                    }
                    else
                    {
                        if (cultureInfo.Name == DefaultCultureInfo.Name)
                        {
                            //当当前语言与默认语言一致时，即调试语言资源未关闭时，不返回未找寻到翻译结果的标记，返回值与Key一致；
                            if (!RunTimeInfo.IsDebugLanguageResource || !RunTimeInfo.IsDebugLanguageResource || isReturnNoResourceValueParcelString == false)
                            {
                                res = key;
                            }
                        }
                        if (RunTimeInfo.IsDebugLanguageResource)
                        {
                            //该行为可能导致线程阻塞
                            NoResourceCache.AddDataItem(key, cultureInfo, AppTime.Now);
                        }
                    }
                }
                catch(Exception ex)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendLine($"从语言资源中获取本地化语言失败：");
                    sb.AppendLine($"Key => {key}");
                    sb.AppendLine($"CultureInfo => {cultureInfo?.Name}");
                    sb.AppendLine($"TryParents => { tryParents }");
                    AppTrace.Logger.LogError(ex, sb.ToString());
                }
            }
            return res;
        }

        /// <summary>
        /// 使用StringFormat进行翻译格式化
        /// </summary>
        /// <param name="stringFormat"> <see cref="string.Format(string, object?[])"/>的第一个参数 </param>
        /// <param name="args"><see cref="string.Format(string, object?[])"/>的第二个参数</param>
        /// <returns></returns>
        public static string StringFormatArgs(
            string stringFormat,
            params object?[] args)
        {
            return StringFormat(stringFormat, args: args);
        }

        /// <summary>
        /// 使用StringFormat进行翻译格式化
        /// </summary>
        /// <param name="stringFormat"></param>
        /// /// <param name="cultureInfo">指定转换的区域特性</param>
        /// <param name="tryParents">
        /// true 表示使用资源回退加载相应资源（如果找不到资源）；
        /// false 表示绕过资源回退进程;
        /// <see cref="ResourceSet.GetString(string, bool)"/>
        /// </param>
        /// /// <param name="isConverterArgs">是否转换参数，默认值true，当明知转换的参数不可能有语言转换时（例如：输出异常信息），该值建议为false，以节省性能</param>
        /// <param name="isBindingConvertSource">是否为BindingConvert的转换源</param>
        /// <param name="args"><see cref="string.Format(string, object?[])"/>的第二个参数</param>
        /// <returns></returns>
        public static string StringFormat(
            string stringFormat, 
            CultureInfo? cultureInfo = default, 
            bool tryParents = false, 
            bool isConverterArgs = true,
            bool isBindingConvertSource = false, 
            params object?[] args)
        {
            var stringFormatResourceValue = stringFormat;
            if (isBindingConvertSource == false)
            {
                stringFormatResourceValue = GetResourceValue(stringFormat, cultureInfo, tryParents);
            }
            if(!string.IsNullOrEmpty(stringFormatResourceValue))
            {
                stringFormat = stringFormatResourceValue;
            }
            List<object?> argsResouceValue = new List<object?>();
            if (args != null)
            {
                foreach (object? arg in args)
                {
                    string? argString = arg?.ToString();
                    if (!string.IsNullOrEmpty(argString) && isConverterArgs)
                    {
                        argsResouceValue.Add(GetResourceValue(arg?.ToString(), cultureInfo, tryParents));
                    }
                    else if(!isConverterArgs)
                    {
                        argsResouceValue.Add(arg);
                    }
                    else
                    {
                        argsResouceValue.Add(null);
                    }
                }
            }
            stringFormat = stringFormat.StringFormatStringStand(argsResouceValue, 2);
            string res = stringFormat;
            try
            {
                res = string.Format(stringFormat, argsResouceValue.ToArray());
            }
            catch(Exception ex)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"调用本地语言资源格式化(LanguageResource.StringFormat)失败");
                sb.AppendLine($"入参：");
                sb.AppendLine($"stringFormat：");
                sb.AppendLine($"{stringFormat}");
                sb.AppendLine($"cultureInfo：");
                sb.AppendLine($"{JsonHelper.EexceptionSerialize(cultureInfo)}");
                sb.AppendLine($"tryParents：");
                sb.AppendLine($"{tryParents}");
                sb.AppendLine($"args：");
                sb.AppendLine($"{JsonHelper.EexceptionSerialize(args)}");
                AppTrace.Logger.LogError(ex, sb?.ToString());
            }
            return res;
        }
    }
}
